import core.*;
import core.util.*;

class util.Timer
{
	private static var paused:Boolean = false;
	private static var pauseLevels:Array;
	
	private static var schedulerList:Array;
	private static var timerValue:Number;
	private static var interval:Number;
	private static var lastTimePassesTimer:Number;

	private static var occurrence:Number = 10;
	
	public static var LEVEL_1:Number = 0;
	public static var LEVEL_2:Number = 1;

	public static var activeLevel:Number = 0;

	private static function start():Void
	{
		//verifica para que o Timer nao crie duas ocorrencias em setInterval
		if (interval == null)
			reset();
	}

	private static function reset():Void
	{
		schedulerList = new Array();
		resetTime();
		
		lastTimePassesTimer = getTimer();
		// define qual a ocorrencia (intervalo) que do timer tera, inicialmente 10 ms
		interval = setInterval(timePasses, occurrence);
		pauseLevels = new Array(false,false);

	}

	// Reseta todo os cronometros dos eventos agendados
	public static function resetTime():Void
	{
		for (var i = schedulerList.length - 1; i >= 0; i--)
			schedulerList[i][2] = 0;

	}
	
	//Metodo executado a cada intervalo de tempo definido em reset
	public static function timePasses():Void
	{
		trace(getTimer());
		//Se o nivel de evento for maior que o nivel de pause ele deve rodar
		//for (var i = schedulerList.length - 1; i >= 0; i--)
		for (var i in schedulerList)
		{
			// se nao estiver pausado e o nivel nao estiver pausado ou nao tiver nivel definido, conta o tempo
			if( !paused && (!pauseLevels[schedulerList[i][5]]) )
				schedulerList[i][2] += occurrence;
		}
		clearInterval(interval);
		interval = setInterval(timePasses, occurrence);
		runScheduler();
		//updateAfterEvent();
	}

	public static function pause(pause:Boolean):Void
	{
		start();
		if (pause == null)
			paused = true;
		else
			paused = pause;
	}
	
	public static function getPauseInLevel(level:Number):Boolean
	{
		return pauseLevels[level];
	}
	
	public static function getPause():Boolean
	{
		return paused;
	}
	
	public static function isPaused():Boolean
	{
		if (paused)
			return true;
		else
			return false;
	}
	
	public static function pauseInLevel(level:Number, pause:Boolean):Void
	{
		start();
		if(level != null)
			pauseLevels[level] = pause;
	}

	//executa uma unica vez no tempo configurado
	public static function scheduler(object:Object, method:Function, seconds:Number, args:Array, level:Number):Void
	{
		start();
		if(level == null)
			level = LEVEL_1;

		//     			   [objeto, metodo, 0, executar em X ms, parametros para o metodo, nivel de pause]
		schedulerList.push([object, method, occurrence, seconds * 1000, args ? args : new Array(), level]);
	}
	
	public static function asynchronize(object:Object, method:Function, args:Array):Void
	{
		scheduler(object, method, 0.000000000001, args, LEVEL_2);
	}

	public static function removeThis(object:Object, method:Function):Boolean
	{
		if (schedulerList!=null)
			for (var i = schedulerList.length - 1; i >= 0; i--)
				if (schedulerList[i][0] == object && schedulerList[i][1] == method)
				{
					schedulerList.splice(i, 1);
					return true;
				}
		return false;
	}

	public static function removeMe(object:Object)
	{
		if (schedulerList != null)
			for (var i:Number = schedulerList.length -1; i >= 0; i--)
			{
				if (schedulerList[i][0] == object)
				{
					schedulerList.splice(i, 1);
				}
			}
	}

	public static function clearScheduler()
	{
		schedulerList = new Array();
	}

	public static function runScheduler():Void
	{
		var objects:Array = new Array();
		var level:Boolean;
	
		for (var i = schedulerList.length - 1; i >= 0; i--)
		{
			var run:Boolean;
			run = schedulerList[i][2] >= schedulerList[i][3];
			if (run)
			{
				//cria uma segunda segunda lista somente com os eventos que devem ser disparados
				objects.push([schedulerList[i][0], schedulerList[i][1], schedulerList[i][4]]);
				schedulerList.splice(i, 1);//tira da agenda
			}
		}
		
		//executa todos os eventos que devem ser disparados na ordem de chegada
		for (var i:Number = objects.length-1; i >=0; i--)
			objects[i][1].apply(objects[i][0], objects[i][2]);
	}
	
	public static function getOccurrence():Number 
	{
		return occurrence;
	}

	public static function setOccurrence(p_occurrence:Number):Void 
	{
		occurrence = p_occurrence;
	}
	
	public static function showScheduler():String
	{
		return schedulerList.toString();
	}
}